/**
 ******************************************************************************
 * @file	bsp_dwt.c
 * @author  Wang Hongxi
 * @author modified by Neo with annotation
 * @version V1.1.0
 * @date    2022/3/8
 * @brief
 */

 #include "bsp_dwt.h"
 #include "cmsis_os.h"
 
 static DWT_Time_t SysTime;
 static uint32_t CPU_FREQ_Hz, CPU_FREQ_Hz_ms, CPU_FREQ_Hz_us;
 static uint32_t CYCCNT_RountCount;
 static uint32_t CYCCNT_LAST;
 static uint64_t CYCCNT64;
 
 /**
  * @brief 私有函数,用于检查DWT CYCCNT寄存器是否溢出,并更新CYCCNT_RountCount
  * @attention 此函数假设两次调用之间的时间间隔不超过一次溢出
  *
  * @todo 更好的方案是为dwt的时间更新单独设置一个任务?
  *       不过,使用dwt的初衷是定时不被中断/任务等因素影响,因此该实现仍然有其存在的意义
  *
  */
 static void DWT_CNT_Update(void)
 {
     static volatile uint8_t bit_locker = 0;
     if (!bit_locker)
     {
         bit_locker = 1;
         volatile uint32_t cnt_now = DWT->CYCCNT;
         if (cnt_now < CYCCNT_LAST)
             CYCCNT_RountCount++;
 
         CYCCNT_LAST = DWT->CYCCNT;
         bit_locker = 0;
     }
 }
 
 void DWT_Init(uint32_t CPU_Freq_mHz)
 {
     /* 使能DWT外设 */
     CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
 
     /* DWT CYCCNT寄存器计数清0 */
     DWT->CYCCNT = (uint32_t)0u;
 
     /* 使能Cortex-M DWT CYCCNT寄存器 */
     DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
 
     CPU_FREQ_Hz = CPU_Freq_mHz * 1000000;
     CPU_FREQ_Hz_ms = CPU_FREQ_Hz / 1000;
     CPU_FREQ_Hz_us = CPU_FREQ_Hz / 1000000;
     CYCCNT_RountCount = 0;
 
     DWT_CNT_Update();
 }
 
 float DWT_GetDeltaT(uint32_t *cnt_last)
 {
     volatile uint32_t cnt_now = DWT->CYCCNT;
     float dt = ((uint32_t)(cnt_now - *cnt_last)) / ((float)(CPU_FREQ_Hz));
     *cnt_last = cnt_now;
 
     DWT_CNT_Update();
 
     return dt;
 }
 
 double DWT_GetDeltaT64(uint32_t *cnt_last)
 {
     volatile uint32_t cnt_now = DWT->CYCCNT;
     double dt = ((uint32_t)(cnt_now - *cnt_last)) / ((double)(CPU_FREQ_Hz));
     *cnt_last = cnt_now;
 
     DWT_CNT_Update();
 
     return dt;
 }
 
 void DWT_SysTimeUpdate(void)
 {
     volatile uint32_t cnt_now = DWT->CYCCNT;
     static uint64_t CNT_TEMP1, CNT_TEMP2, CNT_TEMP3;
 
     DWT_CNT_Update();
 
     CYCCNT64 = (uint64_t)CYCCNT_RountCount * (uint64_t)UINT32_MAX + (uint64_t)cnt_now;
     CNT_TEMP1 = CYCCNT64 / CPU_FREQ_Hz;
     CNT_TEMP2 = CYCCNT64 - CNT_TEMP1 * CPU_FREQ_Hz;
     SysTime.s = CNT_TEMP1;
     SysTime.ms = CNT_TEMP2 / CPU_FREQ_Hz_ms;
     CNT_TEMP3 = CNT_TEMP2 - SysTime.ms * CPU_FREQ_Hz_ms;
     SysTime.us = CNT_TEMP3 / CPU_FREQ_Hz_us;
 }
 
 float DWT_GetTimeline_s(void)
 {
     DWT_SysTimeUpdate();
 
     float DWT_Timelinef32 = SysTime.s + SysTime.ms * 0.001f + SysTime.us * 0.000001f;
 
     return DWT_Timelinef32;
 }
 
 float DWT_GetTimeline_ms(void)
 {
     DWT_SysTimeUpdate();
 
     float DWT_Timelinef32 = SysTime.s * 1000 + SysTime.ms + SysTime.us * 0.001f;
 
     return DWT_Timelinef32;
 }
 
 uint64_t DWT_GetTimeline_us(void)
 {
     DWT_SysTimeUpdate();
 
     uint64_t DWT_Timelinef32 = SysTime.s * 1000000 + SysTime.ms * 1000 + SysTime.us;
 
     return DWT_Timelinef32;
 }
 
 void DWT_Delay(float Delay)
 {
     uint32_t tickstart = DWT->CYCCNT;
     float wait = Delay;
 
     while ((DWT->CYCCNT - tickstart) < wait * (float)CPU_FREQ_Hz)
         ;
 }
 